package org.threebitsoftware.tbb.engine;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Vector;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.threebitsoftware.dao.generated.AppModules;
import org.threebitsoftware.dao.generated.AppModulesKey;
import org.threebitsoftware.dao.generated.Phonenumber;
import org.threebitsoftware.dao.generated.PhonenumberKey;
import org.threebitsoftware.dao.generated.orm.*;
import org.threebitsoftware.tbb.dao.Address;
import org.threebitsoftware.tbb.dao.Contact;
import org.threebitsoftware.tbb.dao.DaoHelper;
import org.threebitsoftware.tbb.dao.EmailAddress;
import org.threebitsoftware.tbb.dao.PhoneNumber;
import org.threebitsoftware.tbb.dao.TagForContact;
import org.threebitsoftware.tbb.dao.bridge.AddressBridge;
import org.threebitsoftware.tbb.dao.bridge.ContactBridge;
import org.threebitsoftware.tbb.dao.bridge.EmailAddressBridge;
import org.threebitsoftware.tbb.dao.bridge.PhoneNumberBridge;
import org.threebitsoftware.tbb.dao.bridge.TagBridge;
import org.threebitsoftware.tbb.module.fileio.FileImportInterface;
import org.threebitsoftware.tbb.module.fileio.FileOutputInterface;
import org.threebitsoftware.tbb.module.photoservice.PhotoInterface;
import org.threebitsoftware.tbb.module.util.MergeLogicInterface;
import org.threebitsoftware.tbb.module.webservice.WebServiceInterface;
import org.threebitsoftware.dao.generated.*;

import com.google.gdata.client.GDataProtocol.Query;

/*
 * Created: Nov 21, 2009
 * Original Author: Elwin
 * Project:
 * Copyright:
 * 
 * Purpose:
 */

/*
 * Copyright (c) 2009, 3BitSoftware
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the <organization> nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

public class EngineUtil 
	implements MergeLogicInterface {
	private static Logger cLogger = Logger.getLogger(EngineUtil.class);
	private static int idAppUser_=-1;
	// This variable defines whether the database is used; set this to false
	// to not use the database.
	boolean underTestUseStorage = true;
	
	private static final String L_SQL = "SELECT ID_APP_MODULES FROM APP_MODULES WHERE S_INTERFACE = ? AND I_ENABLED = 1;";
	private static final String C_STR_DEFAULT_MERGE_CLASS = "org.threebitsoftware.tbb.engine.EngineUtil";
	
	private static final float C_F_PERCENT_STORED_SUCCESSFUL = 0.5f;
	 
	private static final int UPLOAD_ONLY = 0;
	private static final int DOWNLOAD_ONLY = 1;
	private static final int BOTH_UPLOAD_AND_DOWNLOAD = 2;
	/**
	 * @see getAppModule
	 * @return
	 */
	public static HashMap getFileImportHandlers() {
		cLogger.debug("Attemting to get file import handlers");		
		return getAppModule(FileImportInterface.class.getName());		
	}
	
	/**
	 * @see getAppModule
	 * @return
	 */
	public static HashMap getFileExportHandlers() {
		cLogger.debug("Attemting to get file export handlers");
		return getAppModule(FileOutputInterface.class.getName());
	}
	
	/**
	 * Retrieves a hash map which contains a class name as a key and short 
	 * description as the value.
	 * @param myInterfaceClass ; The full name of the interface.
	 * @return A HashMap of String:String; the key is a full class name;
	 * the value is a description of the class.
	 */
	public static HashMap getAppModule(String myInterfaceClass) {
		
		cLogger.debug("Request to get the handlers for interface: " 
				+ myInterfaceClass);
		
		HashMap lHashMap = null;
		ResultSet lRS = null;
		Connection lConn = DaoHelper.getDBConnection();
		PreparedStatement lPS = null;
		
		try {
			lPS = lConn.prepareStatement(L_SQL);
			lPS.setString(1, myInterfaceClass);
			lRS = lPS.executeQuery();
			
			cLogger.debug("Attempting to get the result set");
			
			if(lRS != null)
			{
				int liSize = lRS.getFetchSize();
				cLogger.debug("Recieved " + liSize + " results.");
				
				if(liSize > 0)
				{
					int liIds[] = new int[liSize];
					for(int i = 0; i < liSize && lRS.next(); i++)
					{
						liIds[i] = lRS.getInt(1);
						if(cLogger.isDebugEnabled())
						{
							cLogger.debug("Found App Module ID: " + liIds[i]);
						}
					}
					
					//could combine the next step and the previous; want to
					//close connections though.
					
					cLogger.debug("Done getting the ids.  Now getting the modules");
					lRS.close();
					lPS.close();
					
					AppModulesDAOImpl lDAO = new AppModulesDAOImpl();
					
					for(int i = 0; i < liSize; i++)
					{
						if(cLogger.isDebugEnabled())
						{
							cLogger.debug("Attempting to get file import object:" + liIds[i]);
						}
						AppModulesKey lKey = new AppModulesKey();
						lKey.setIdAppModules(liIds[i]);
						
						AppModules lAppModule = lDAO.load(lKey, lConn);
						lHashMap.put(lAppModule.getSClassname(), lAppModule.getSCat1());
					}
					
					cLogger.debug("Done with getting the app modules.  Found: "
							+ lHashMap.size());
				}
				
			}
			else
			{
				cLogger.error("NULL returned for result set looking up" + 
						myInterfaceClass);
				throw new SQLException("Recieved a NULL ResultSet");
			}
		}
		catch(SQLException sqlex)
		{
			cLogger.error("An error occured running statement: " + L_SQL, sqlex);
			lHashMap = null;
		}
		
		cLogger.debug("Finished.");
		
		return lHashMap;
	}
	
	/**
	 * Return logged in user application ID
	 * @param myUsername
	 * @return
	 * @throws SQLException
	 */
	public static AppUser getIdAppUser(String myUsername) throws SQLException{
		
		//TODO: Call the DB to get the userid for the logged in user.
		AppUserDAOImpl lDAO = new AppUserDAOImpl();
		AppUserKey lKey = new AppUserKey();
		lKey.setIdAppUsername(myUsername);
		
		AppUser loggedInUserKey = null;
		Connection myConn = DaoHelper.getDBConnection();
		AppUser currentAppUser = lDAO.load(lKey, DaoHelper.getDBConnection());
		
		if (currentAppUser!=null){//found app user.
		idAppUser_ = (int) currentAppUser.getIdAppUser();
		loggedInUserKey = currentAppUser;
		}
		else if (currentAppUser==null){//could not find app user
			loggedInUserKey = new AppUser();
			loggedInUserKey.setIdAppUser(-1);
			
		}
		return loggedInUserKey;
	}

	
	/**
	 * Add or update the user in the database
	 * @param myUsername
	 * @param myPassword
	 * @param queryType
	 * @throws SQLException
	 */
	public static void setIdAppUser(String myUsername, String myPassword, String queryType) throws SQLException{
		
		//Call the DB to set new user with the username.
		AppUserDAOImpl lDAO = new AppUserDAOImpl();
		AppUser newAppUser = new AppUser();
		
		newAppUser.setSNameFirst("");
		newAppUser.setSNameLast("");
		newAppUser.setSUsername(myUsername);
		newAppUser.setSPassword(myPassword);
		
		
		if (queryType.equals("update")){
			lDAO.update(newAppUser, DaoHelper.getDBConnection());
			
		} 
		else if (queryType.equals("insert")){
			lDAO.create(newAppUser, DaoHelper.getDBConnection());
		}
	}
	
	
	//TODO: I don't think we need this method.
	public static void setIdAppUser(int idAppUser){
		idAppUser_ = idAppUser;
	}
	
	/**
	 * Imports contacts from a file defined by the path into the storage using
	 * the class defined.
	 * @param myClassName; Class defined must implement FileImportInterface.
	 * @param myFilePath; The full path of the file to be imported.
	 * @return
	 */
	public static boolean useFileImportHandler(String myClassName, String myFilePath) {
		boolean lbWorked = true;
		FileImportInterface lFileImport = null;
		Contact lContacts[] = null;
		
		lFileImport = (FileImportInterface)instantiateObject(myClassName);
		if(lFileImport == null)
		{
			cLogger.error("Could not load FileImportInterface class: " + myClassName);
			lbWorked = false;
		}
		
		//No sense if going further if we have already failed.
		if(lbWorked)
		{
			try {
				lContacts = lFileImport.parseFile(myFilePath);
			}
			catch(Exception ex){
				cLogger.error(ex, ex);
				lbWorked = false;
			}
		}
		
		//No sense if trying to merge if we have already failed.
		if(lbWorked)
		{
			lbWorked = mergeContacts(lContacts, lFileImport.getMergeClassName());			
		}
		
		return lbWorked;
		
	}
	
	/**
	 * A wrapper method to help extract the merge interface logic handling.
	 * @param myContacts; an array of contacts; this is replaced when a merge
	 * 		occurs. 
	 * @param myClassName;  A class name; if it does not use implement the
	 * 		MergeLogicInterface then the default is chossen.
	 * @return true if successful; false otherwise.
	 */
	public static boolean mergeContacts(Contact[] myContacts, String myClassName)
	{
		boolean lbWorked = true;
		MergeLogicInterface lMergeInstance = null;
		
		if(myClassName == null || myClassName.isEmpty())
		{
			// Check early on if it is possible to use this.
			myClassName = C_STR_DEFAULT_MERGE_CLASS;
		}
		
		try {
			lMergeInstance = (MergeLogicInterface)instantiateObject(myClassName);
		}
		catch (ClassCastException ccex)
		{
			cLogger.info(myClassName + " does no thave its own merge logic.  Attempting the default.");
			lMergeInstance = null;
		}
		
		if(lMergeInstance == null)
		{
			cLogger.warn("Defaulting to implementation in: " + C_STR_DEFAULT_MERGE_CLASS);
			lMergeInstance = (MergeLogicInterface)instantiateObject(C_STR_DEFAULT_MERGE_CLASS);
		}
		
		Contact lContacts[] = getAllContacts();
		Contact lNewContacts[] = lMergeInstance.mergeContacts(myContacts, lContacts);
		
		//Replace object pointer to contact list.
		myContacts = lNewContacts;
		
		return lbWorked;
	}
	
	
	
	
	/**
	 * This method uses the named file to send contacts to.  The contacts
	 * are written by means of the named class.
	 * @param myClassName; Class which must implement the FileOutputInterface.
	 * @param myFilePath;  Full file path of the destination file.
	 * @param myObjects; The array of contacts to be exported.
	 * @return true is worked; false otherwise.
	 */
	public static boolean useFileExportHandler(String myClassName, 
			String myFilePath, Contact[] myObjects)
			throws ClassNotFoundException
	{
		FileOutputInterface lFileOutput = null;
		boolean lbWorked = true;
		
		/*
		if(myObjects == null || (myObjects != null && myObjects.length == 0))
		{
			// nothing to do.
			cLogger.info("A request to export 0 contacts with " + myClassName
					+ "was made.");
			return true;  // noting to do; do 
		}
		*/
		
		lFileOutput = (FileOutputInterface)instantiateObject(myClassName);
		if(lFileOutput == null) {
			cLogger.error("Unsuccessfully retrieved FileOutputInterface in"
					+ " class" + myClassName);
			throw new ClassNotFoundException();
		}
		
		try {
			
			// test for multiple contact export
			if(lFileOutput.canExportMultipleContacts())
			{
				int liSize = myObjects.length;
				lbWorked = lFileOutput.exportContactToFile(myFilePath,
						myObjects);
			}
			else if(myObjects.length > 1)
			{
				throw new RuntimeException("Selected method: "
						+ myClassName + " may only export a single contact.");
			}
			else  
			{
				// fileExport class can only handle one contact and there is
				// only one contact
				
				lbWorked = lFileOutput.exportContactToFile(myFilePath, 
						myObjects[0]);
			}
			
		}
		catch(Exception ex)
		{
			cLogger.error("Exception thrown while exporting the contacts."
					+ " | File Path: " + myFilePath
					+ " | Class: " + myClassName
					+ " | Contacts: " + (myObjects == null?"NULL":" NOT NULL"));
			lbWorked = false;
		}
		
		
		cLogger.debug("Returning : " + lbWorked);
		return lbWorked;
	}
	
	/**
	 * Retrieves the classes capable of use the WebServiceInterface
	 * interface.
	 * @return HashMap of strings; key is class name; value is description.
	 */
	public static HashMap getWebServiceHandler() {
		HashMap lHashMap = null;
		cLogger.debug("Attempting to get a list of webservices.");
		lHashMap = getAppModule(WebServiceInterface.class.getName());
		cLogger.debug("Retrieved web services: " + (lHashMap == null?"NULL":lHashMap.size()));
		
		return lHashMap;
	}

	
	/**
	 * Unimplemented.
	 * 
	 * Method makes a call to the database to see if the username and password
	 * are there.  Performs a synchronization with the web service after that.
	 * @param myClassName; the class name which is to be used.
	 * @return
	 */
	public static boolean useWebServiceHandler(String myClassName) {
		boolean lbWorked = false;
		
		return lbWorked;
	}
	
	
	/**
	 * Get the users web service account information.
	 * @param myClassName; Class which implements WebServiceInterface.
	 * @param myUserName; Username of the user.
	 * @param myPassword; Password of the user.
	 * @param myDirection; 0 = Merge; -1 = download only; 1 = upload only
	 * @return true if successful, false otherwise.
	 * @return true if successful, false otherwise.
	 */
	public static AppExternalservices getWebServiceUserAccountInfo (String appUserName){
		AppExternalservices lDaoObject = null;
		cLogger.debug("Getting a AppExternalservices DAO object for id: " + appUserName);
		
		AppExternalservicesDAOImpl lDAO = new  AppExternalservicesDAOImpl();
		
		AppExternalservicesKey lKey = new AppExternalservicesKey();		
		lKey.setIdAppUserEmail(appUserName);
		
		try {
			lDaoObject = lDAO.load(lKey, DaoHelper.getDBConnection(), "byserviceusername");
		}
		catch(SQLException sqlex)
		{
			cLogger.error("Error retrieving phone number with id:" + appUserName, sqlex);
		}
		
		return lDaoObject;
		
	}

	
	public static boolean setWebServiceUserAccountInfo (AppExternalservices updatedAppUserInfo){
		DaoHelper.getDBConnection();
		boolean status = false;
		cLogger.debug("Getting a AppExternalservices DAO object for id: " + updatedAppUserInfo.getIdAppUser());

		AppExternalservicesDAOImpl lDAO = new  AppExternalservicesDAOImpl();
		
		AppExternalservicesKey lKey = new AppExternalservicesKey();		
		lKey.setIdAppUserEmail(updatedAppUserInfo.getSUsername());
		
		try {
			
			AppExternalservices checkAppUserInfo = lDAO.load(lKey, DaoHelper.getDBConnection(), "byserviceusername");
			
			if (checkAppUserInfo ==null){// does not exist add the account
				lDAO.create(updatedAppUserInfo, DaoHelper.getDBConnection());
				status = true;
				
			} else if (checkAppUserInfo!=null){//exist update the account
				lDAO.update(updatedAppUserInfo, DaoHelper.getDBConnection());
				status = true;
			}
		}
		catch(SQLException sqlex)
		{
			cLogger.error("Error retrieving phone number with id:" + updatedAppUserInfo.getIdAppUser(), sqlex);
		}
		
		
		return status;
		
	}

	
	public static boolean useWebServiceHandler(String myClassName, 
			String myUserName, String myPassword, int myDirection) {
		
		Contact lDownloadedContacts[] = null;
		WebServiceInterface lWS = null;
		boolean lbWorked = true;
		
		
		cLogger.debug("Attempting a web service sync with the parameters:" 
				+ "\nClass: " + myClassName
				+ "\nUsername: " + myUserName
				+ "\nPassword: " 
				+ (myPassword != null && !myPassword.isEmpty()?" ***** ":" No password entered.")
				+ "\nDirection: " + myDirection);
		
		lWS = (WebServiceInterface) instantiateObject(myClassName);
		
		try {
			lWS.initializeParams(myUserName, myPassword, null);
			
			
			// Merge first then decide what to do.
			lDownloadedContacts = lWS.getContacts();
			
			lbWorked = mergeContacts(lDownloadedContacts, myClassName);
			if(!lbWorked)
			{
				cLogger.warn("Intial merge failed merge failed.");
			}
			
			
			// NOT down load only
			if(myDirection == UPLOAD_ONLY)
			{
				lWS.publishContacts(lDownloadedContacts);
			}
			
			// NOT upload only.
			if(myDirection == DOWNLOAD_ONLY)
			{
				lbWorked = storeContact(lDownloadedContacts);
			}
			
			if(myDirection == BOTH_UPLOAD_AND_DOWNLOAD)
			{
				lbWorked = storeContact(lDownloadedContacts);
				lWS.publishContacts(lDownloadedContacts);
			}
			
		}
		catch(SQLException sqlex)
		{
			cLogger.error("SQL Exception during WebService.", sqlex);
			lbWorked = false;
		}
		catch(Exception ex)
		{
			cLogger.error("Unspecific exception during web service synch.", ex);
			lbWorked = false;
		}
		
		
		if(!lbWorked)
		{
			cLogger.warn("Failed attempt at a web service sync with the parameters:" 
					+ "\nClass: " + myClassName
					+ "\nUsername: " + myUserName
					+ "\nPassword: " 
					+ (myPassword != null && !myPassword.isEmpty()?" ***** ":" No password entered.")
					+ "\nDirection: " + myDirection);
		}
		
		if(cLogger.isDebugEnabled())
		{
			cLogger.debug("Done with useWebServiceHandler");
		}
		
		return lbWorked;
	}
	
	
	/**
	 * Stores and array of contacts.
	 * @param myContact; an array of contacts.
	 * @return true if worked; false otherwise.
	 */
	public static boolean storeContact(Contact myContact[]) {
		boolean lbWorked = true;
		int liNumberWorked = 0;
		
		cLogger.debug("Request to store an array of contacts");
		if(myContact == null)
		{
			cLogger.info("Nothing to do; recieved a null array of contacts.");
		}
		else
		{
			int liSize = myContact.length;
			cLogger.debug("Contacts requiring storage: " + liSize);
			
			for(int i = 0; i < liSize; i++)
			{
				if(storeContact(myContact[i]))
				{
					liNumberWorked++;
				}
			}
			
			//Determine if whole function call successful.
			float lfPercentWorked = liSize / liNumberWorked;
			cLogger.debug("Done storing.  Number which worked: " 
					+ liNumberWorked + " out of " + liSize);
			cLogger.debug("Required Success: " + C_F_PERCENT_STORED_SUCCESSFUL
					+ " Actual: " + lfPercentWorked);
			if(lfPercentWorked < C_F_PERCENT_STORED_SUCCESSFUL)
			{
				lbWorked = false;
			}
			
		}
		
		return lbWorked;
		
	}
	
	public static String[] getDistinctContact()
	{
		return ContactBridge.getDistinctTags();
	}
	
	/**
	 * Stores a single contact.
	 * @param myContact
	 * @return
	 */
	public static boolean storeContact(Contact myContact) {
		boolean lbWorked = true;
		
		cLogger.debug("Attempting to store a contact.");
		
		if(myContact == null)
		{
			cLogger.info("Attempted to store a NULL contact.");
		}
		//Check to make sure we are really going to store it.
		else 
		{
			boolean lbDeleteContact = myContact.isMarkedForDeletion();
			// Store the contact first.
			// Must be done in order to get an ID for a new contact.
			
			cLogger.debug("Starting to store the contact with ID: " 
					+ myContact.getIdContact());
			myContact.setIdAppUser(idAppUser_);
			if(!lbDeleteContact)
			{
				ContactBridge.storeContact(myContact);
			}
			
			int liIdContact = myContact.getIdContact();
			
			cLogger.debug("Done storing just the contact with id: " + liIdContact);
			
			
			Object lObjAddress[] = null;
			Object lObjEmail[] = null;
			Object lObjPhone[] = null;
			Object lObjTags[] = null;
			
			Vector lVectorTemp = myContact.getAddresses();
			if(lVectorTemp != null && lVectorTemp.size() > 0) lObjAddress = lVectorTemp.toArray();
			
			lVectorTemp = myContact.getEmails();
			if(lVectorTemp != null && lVectorTemp.size() > 0) lObjEmail = lVectorTemp.toArray();
			
			lVectorTemp = myContact.getPhoneNumbers();
			if(lVectorTemp != null && lVectorTemp.size() > 0) lObjPhone = lVectorTemp.toArray();
			
			lVectorTemp = myContact.getTags();
			if(lVectorTemp == null || (lVectorTemp != null && lVectorTemp.size() == 0))
			{
				cLogger.info("Adding the contact to the Default group.");
				lVectorTemp = new Vector(1);
				TagForContact lTempTag = new TagForContact();
				lTempTag.setSTag("Default");
				lVectorTemp.add(lTempTag);
			}
			
			if(lVectorTemp != null && lVectorTemp.size() > 0) lObjTags = lVectorTemp.toArray();
			
			//Store addresses
			for(int i = 0; lObjAddress!=null && i < lObjAddress.length; i++)
			{
				Address lAddressTemp = (Address)lObjAddress[i];
				
				if(lbDeleteContact)
				{
					lAddressTemp.markForDeletion(lbDeleteContact);
				}
				lAddressTemp.setIdContact(liIdContact);
				lAddressTemp.setIdAppUser(idAppUser_);
				AddressBridge.storeAddress(lAddressTemp);
			}
			
			//Store email addresses.
			for(int i = 0; lObjEmail != null && i < lObjEmail.length; i++)
			{
				EmailAddress lEmailTemp = (EmailAddress)lObjEmail[i];
				if(lbDeleteContact)
				{
					lEmailTemp.markForDeletion(lbDeleteContact);
				}
				lEmailTemp.setIdContact(liIdContact);
				lEmailTemp.setIdAppUser(idAppUser_);
				EmailAddressBridge.storeEmailAddress(lEmailTemp);
			}
			
			//Store phone numbers.
			for(int i = 0; lObjPhone != null && i < lObjPhone.length; i++)
			{
				PhoneNumber lPNTemp = (PhoneNumber)lObjPhone[i];
				if(lbDeleteContact)
				{
					lPNTemp.markForDeletion(lbDeleteContact);
				}
				lPNTemp.setIdContact(liIdContact);
				PhoneNumberBridge.storePhoneNumber(lPNTemp);
			}
			
			// Store the tags.
			for(int i = 0; lObjTags != null && i < lObjTags.length; i++)
			{
				TagForContact lTagTemp = (TagForContact)lObjTags[i];
				if(lbDeleteContact)
				{
					lTagTemp.markForDeletion(lbDeleteContact);
				}
				lTagTemp.setIdContact((new Integer(liIdContact)).longValue());
				TagBridge.storeTag(lTagTemp);
			}
			
			if(lbDeleteContact)
			{
				ContactBridge.storeContact(myContact);
			}
			
		}
		
		
		return lbWorked;
		
	}
	
	
	public static Contact getContact(int myId) {
		Contact lContactReturned = null;
		
		return lContactReturned;
	}
	
	
	public static Contact[] getContact(String myQuery) throws NullPointerException{
		Contact lContactReturned[] = null;
			
		lContactReturned = ContactBridge.getContacts(myQuery);
		if(lContactReturned == null || (lContactReturned != null && lContactReturned.length == 0) ||(lContactReturned[0]==null))
		{
			cLogger.info("Did not retreive any contacts with query: " + myQuery);
			lContactReturned =  new Contact[0];
		}
		else
		{
			// we have something to work on
			int liSize = lContactReturned.length;
			
			cLogger.debug("Attempting to get the rest of the information for num contacts: " + liSize);
			
			for(int ic = 0; ic < liSize; ic++)
			{
				cLogger.debug("Working on contact " 
						+ lContactReturned[ic].getIdContact() 
						+ " for app user " + idAppUser_);
				fillContactInfo(lContactReturned[ic]);
				cLogger.debug("Done with filling the contact.");
			}
		}
		
		return lContactReturned;
	}
	
	
	public static Contact[] getAllContacts() throws NullPointerException{
		Contact lContactList[] = null;
		
		cLogger.debug("Entered getAllContacts");
		
		lContactList = getContact("ID_APP_USER=" + idAppUser_);
		
		cLogger.debug("Done getting all the contacts using the auxilary method.");
		
		return lContactList;
	}

	
	@Override
	public Contact[] mergeContacts(Contact[] myNewContact,
			Contact[] myOldContact) {
        Contact[] combinedContacts = new Contact[myNewContact.length + myOldContact.length];

        System.arraycopy(myNewContact,0,combinedContacts,0,myNewContact.length);

        System.arraycopy(myOldContact,0,combinedContacts,myNewContact.length,myOldContact.length);

   
          return combinedContacts;
	}
	
	
	public static void deleteContact (Contact myContact) throws SQLException{
		cLogger.info("Attempting to delete a contact.");
		if (myContact!=null){
			cLogger.info("Contact " + myContact.toString());
			myContact.markForDeletion(true);
			storeContact(myContact);
		} else if (myContact==null){
			cLogger.info("Contact is null. Nothing to delete");
		}
		cLogger.info("Done deleting");
	}
	
	
	/**
	 * Get the Contact's photo.
	 * @param myContact
	 * @return
	 * @throws IOException 
	 */
	public static Contactphoto getPhotoExport(Contact myContact, String myClassName, String destFileName){
		
		
		
		boolean lbWorked = true;
		PhotoInterface lPhotoExport = null;
		
		lPhotoExport = (PhotoInterface)instantiateObject(myClassName);
		
	
		
		ContactphotoDAOImpl lDAO = new ContactphotoDAOImpl();
		ContactphotoKey lKey = new ContactphotoKey();
		lKey.setIdContact(myContact.getIdContact());
		
		Contactphoto currentContactPhoto = null;
		
		try {
			currentContactPhoto = lDAO.load(lKey, DaoHelper.getDBConnection());
			
			if (currentContactPhoto!=null){//Contact has a photo so display it
				lPhotoExport.exportPhoto(currentContactPhoto.getBlobPhoto(), destFileName);
			} 
			

			
		} catch (SQLException sqlErrorPhoto) {
			cLogger.error("Error while getting the photo. " + sqlErrorPhoto.getMessage());
			sqlErrorPhoto.printStackTrace();
		}
		return currentContactPhoto;
		
	}
	
	
	/**
	 * sets photo for a contact. 
	 * @param myContact
	 * @return
	 */
	public static boolean setPhotoImport(Contactphoto myContact,String myClassName, String photoLocation, String quertType){
		
		boolean lbWorked = true;
		PhotoInterface lPhotoImport = null;
		
		lPhotoImport = (PhotoInterface)instantiateObject(myClassName);
		
		
		//save the Contact photo
		myContact.setBlobPhoto(lPhotoImport.getImportPhoto(photoLocation));
		
		ContactphotoDAOImpl lDAO = new ContactphotoDAOImpl();
		
		Contactphoto currentContactPhoto = null;
		
		try {
			
			if (quertType.equals("insert")){//No photo exist for the user so save one.
				lDAO.create(myContact, DaoHelper.getDBConnection());
				lbWorked = true;
				
			} else if (quertType.equals("update")){//Update the photo with a new one.
				lDAO.update(myContact, DaoHelper.getDBConnection());
				lbWorked = true;
			}

			
		} catch (SQLException sqlErrorPhoto) {
			cLogger.error("Error while getting the photo. " + sqlErrorPhoto.getMessage());
			sqlErrorPhoto.printStackTrace();
		}
		 return lbWorked;
	}
	
	
	
	public HashMap<String, Contact> getContactByTagName (String myQuery){
		
		//The query for this should be like :
		//Select * from Contact where tag=%cVectorTags% and iIdAppUser=%iIdAppUser%
		
		return null;
		
	}
	
	
	/* ******************************************
	 * Utility Methods
	 * ******************************************
	 */
	
	/**
	 * Handles creating a new object from a class name.
	 */
	static Object instantiateObject(String myClassName)
	{
		cLogger.info("Attempting to instantiate: " + myClassName);
		try {
			Class lClass = Class.forName(myClassName);
			return lClass.newInstance();			
		}
		catch(Exception ex)
		{
			cLogger.error("Could not load class: " + myClassName, ex);
		}
		
		return null;
	}
	
	
	
	/**
	 * Takes and array of objects and creates a Vector.
	 * @param myObjects
	 * @return
	 */
	static Vector convertArrayToVector(Object myObjects[])
	{
		Vector lVector = null;
		cLogger.debug("Attempting to transform an array to a vector.");
		if(myObjects != null )
		{
			int liSize = myObjects.length;
			lVector = new Vector(liSize + 2);
			
			cLogger.debug("Attempting to fill a vector of size: " + liSize);
			for(int i = 0; i < liSize; i++)
			{
				lVector.add(myObjects[i]);
			}
		}
		else
		{
			cLogger.debug("Nothing passed the function.  Creaing a standard Vector");
			lVector = new Vector();
		}
		
		cLogger.debug("Returning a Vector of size: " + lVector.size());
		
		return lVector;
		
	}
	
	
	
	/**
	 * Fills a contact with information.
	 * @param myContact ; Fills this contact with one to many information.
	 */
	static void fillContactInfo(Contact myContact)
	{
		int litcId = myContact.getIdContact();
		String lTempQuery = "ID_CONTACT=" + litcId;
		
		cLogger.debug("Working on contact " + litcId + " for app user " 
				+ idAppUser_+ "with query: " + lTempQuery);
		
		//Get the phone numbers
		PhoneNumber lTempPN[] = PhoneNumberBridge.getPhoneNumbers("ID_CONTACT=" + litcId);
		if(lTempPN != null)
		{
			cLogger.debug("Found " + lTempPN.length + " phone numbers.");
			myContact.setPhoneNumbers(convertArrayToVector(lTempPN));
		}
		else if(cLogger.isDebugEnabled())
		{
			cLogger.debug("Nothing foudn for phone numbers.");
		}
		
		
		//Get the Addresses
		Address lTempAdr[] = AddressBridge.getAddresses(lTempQuery, idAppUser_);
		if(lTempPN != null)
		{
			cLogger.debug("Found " + lTempAdr.length + "addresses.");
			myContact.setAddresses(convertArrayToVector(lTempAdr));
		}
		else if(cLogger.isDebugEnabled())
		{
			cLogger.debug("Nothing found for addresses.");
		}
		
		//Get the emails
		EmailAddress lTempEmail[] = null;
		lTempEmail = EmailAddressBridge.getEmailAddresses(lTempQuery, idAppUser_);
		if(lTempEmail != null)
		{
			cLogger.debug("Found " + lTempEmail.length + " email addresses.");
			myContact.setEmails(convertArrayToVector(lTempEmail));
		}
		else if(cLogger.isDebugEnabled())
		{
			cLogger.debug("Nothing found for email addresses.");
		}
		
		TagForContact lContactTag[] = null;
		lContactTag = TagBridge.getTags(lTempQuery);
		if(lContactTag != null)
		{
			cLogger.debug("Found " + lContactTag.length + " contacts.");
			myContact.setTags(convertArrayToVector(lContactTag));
		}
		else if(cLogger.isDebugEnabled())
		{
			cLogger.debug("Nothing found for tags.");
		}
		
		cLogger.info("Done filling the contact: " + litcId);
	}
}
